package com.kylin.domain.commodity.aggregate;

import com.kylin.domain.authority.command.*;
import com.kylin.domain.commodity.event.*;
import com.kylin.domain.commodity.vo.AlbumVO;
import com.kylin.domain.commodity.vo.CommentVO;
import com.kylin.shared.common.consts.Constants;
import com.kylin.shared.enums.ApprovalState;
import com.kylin.shared.enums.YesNoEnum;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.eventsourcing.EventSourcingHandler;
import org.axonframework.modelling.command.AggregateIdentifier;
import org.axonframework.spring.stereotype.Aggregate;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static org.axonframework.modelling.command.AggregateLifecycle.apply;

/**
 * 商品聚合
 * @author Damon S.
 * @version v1.0.1
 * @date 2019年12月19日 17:01
 */
@Slf4j
@Getter
@Setter(value = AccessLevel.PRIVATE)
@Aggregate
@NoArgsConstructor
public final class CommodityAggregate {
    @AggregateIdentifier
    private Long    spuId;
    private String  spuCode;
    private String  name;
    private String  subtitle;
    private String  coverUrl;
    private Integer inventory;
    private Long    price;
    private String  description;
    /**是否上架*/
    private YesNoEnum shelved;
    private ApprovalState approvalState;
    private Long    merchantId;
    private YesNoEnum removed;
    private Long    createdBy;
    private Long    updatedBy;
    private Long    removedBy;
    private Long    approvedBy;
    private Long    refusedBy;
    private LocalDateTime shelvedAt;
    private LocalDateTime putOffAt;
    private LocalDateTime approvedAt;
    private LocalDateTime refusedAt;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    private LocalDateTime removedAt;

    private List<AlbumVO> albumVO;
    private List<CommentVO> commentVOs;


    @CommandHandler
    public CommodityAggregate(CreateCommodityCommand command) {
        apply(CommodityCreatedEvent.builder()
                .spuId(command.getCommodityId())
                .code(command.getCode())
                .name(command.getName())
                .subtitle(command.getSubtitle())
                .coverUrl(command.getCoverUrl())
                .inventory(command.getInventory())
                .price(command.getPrice())
                .description(command.getDescription())
                .merchantId(command.getMerchantId())
                .approvalState(ApprovalState.DRAFTING)
                .createdBy(command.getCreatedBy())
                .createdAt(LocalDateTime.now())
                .removed(YesNoEnum.N)
                .albumVO(command.getAlbumVO())
                .shelved(YesNoEnum.N).build());
    }

    @EventSourcingHandler
    private void on(CommodityCreatedEvent event) {
        setSpuId(event.getSpuId());
        setSpuCode(event.getCode());
        setName(event.getName());
        setSubtitle(event.getSubtitle());
        setCoverUrl(event.getCoverUrl());
        setInventory(event.getInventory());
        setPrice(event.getPrice());
        setDescription(event.getDescription());
        setMerchantId(event.getMerchantId());
        setRemoved(event.getRemoved());
        setShelved(event.getShelved());
        setApprovalState(event.getApprovalState());
        setCreatedBy(event.getCreatedBy());
        setCreatedAt(event.getCreatedAt());
        setAlbumVO(event.getAlbumVO());
        // 以下初始化为默认值
        setUpdatedBy(Constants.LONG_MINUS_ONE);
        setRemovedBy(Constants.LONG_MINUS_ONE);
        setRefusedBy(Constants.LONG_MINUS_ONE);
        setApprovedBy(Constants.LONG_MINUS_ONE);

        log.info("{}商品[spuCode:{}]对象初始化完成。", Constants.PREFIX_COMMODITY, event.getCode());
    }

    @CommandHandler
    private void handle(UpdateCommodityCommand command) {
        apply(CommodityUpdatedEvent.builder()
                .commodityId(command.getSpuId())
                .name(command.getName())
                .subtitle(command.getSubtitle())
                .coverUrl(command.getCoverUrl())
                .price(command.getPrice())
                .inventory(command.getInventory())
                .description(command.getDescription())
                .removed(command.getRemoved())
                .shelved(command.getShelved())
                .updatedBy(command.getUpdatedBy())
                .updatedAt(LocalDateTime.now()).build());
    }

    @EventSourcingHandler
    private void on(CommodityUpdatedEvent event) {
        setName(event.getName());
        setSubtitle(event.getSubtitle());
        setCoverUrl(event.getCoverUrl());
        setPrice(event.getPrice());
        setInventory(event.getInventory());
        setDescription(event.getDescription());
        setRemoved(event.getRemoved());
        setShelved(event.getShelved());
        setUpdatedBy(event.getUpdatedBy());
        setUpdatedAt(event.getUpdatedAt());
        log.info("{}商品[spuCode:{}]对象更新完成。", Constants.PREFIX_COMMODITY, getSpuCode());
    }

    @CommandHandler
    private void handle(UpdateGalleryCommand command) {
        apply(AlbumUpdatedEvent.builder()
                .albumVO(command.getAlbumVO())
                .updatedBy(command.getUpdatedBy())
                .updatedAt(LocalDateTime.now())
                .spuId(command.getSpuId()).build()
        );
    }

    @EventSourcingHandler
    private void on(AlbumUpdatedEvent event) {
        setAlbumVO(event.getAlbumVO());
    }

    @CommandHandler
    private void handle(CommentCommodityCommand command) {
        apply(CommodityCommentedEvent.builder()
                .content(command.getContent())
                .commentedBy(command.getCommentedBy())
                .commentedAt(LocalDateTime.now())
                .commodityId(command.getCommodityId()).build()
        );
    }

    @EventSourcingHandler
    private void on(CommodityCommentedEvent event) {
        if (!CollectionUtils.isEmpty(commentVOs)) {
            commentVOs.add(CommentVO.builder()
                    .userId(event.getCommentedBy())
                    // TODO: nickname & avatar
                    .content(event.getContent())
                    .createdAt(event.getCommentedAt())
                    .removed(YesNoEnum.N).build());
        }
    }

    @CommandHandler
    private void handle(RemoveCommentCommand command) {
        apply(CommentRemovedEvent.builder()
                .commentId(command.getCommentId())
                .eventBy(command.getRemovedBy())
                .eventAt(LocalDateTime.now())
                .commodityId(command.getCommodityId()).build()
        );

    }

    @EventSourcingHandler
    private void on(CommentRemovedEvent event) {
        if (!CollectionUtils.isEmpty(commentVOs)) {
            setCommentVOs(commentVOs.stream().filter(
                    comment -> !Objects.equals(comment.getCommentId(), event.getCommentId())
            ).collect(Collectors.toList()));
        }
    }

    @CommandHandler
    private void handle(ShelveSpuCommand command) {
        apply(new SpuShelvedEvent(
                command.getSpuId(), command.getUpdatedBy()
        ));
    }

    @EventSourcingHandler
    private void on(SpuShelvedEvent event) {
        if (YesNoEnum.N.equals(getShelved())) {
            setShelved(YesNoEnum.Y);
            setUpdatedBy(event.getUpdatedBy());
            setUpdatedAt(event.getUpdatedAt());
            setShelvedAt(event.getUpdatedAt());
        }
    }

    @CommandHandler
    private void handle(PutOffSpuCommand command) {
        apply(new SpuPutOffedEvent(
                command.getSpuId(), command.getUpdatedBy()
        ));
    }

    @EventSourcingHandler
    private void on(SpuPutOffedEvent event) {
        if (YesNoEnum.Y.equals(getShelved())) {
            setShelved(YesNoEnum.N);
            setPutOffAt(event.getUpdatedAt());
            setUpdatedBy(event.getUpdatedBy());
            setUpdatedAt(event.getUpdatedAt());
        }
    }

    @CommandHandler
    private void handle(RemoveSpuCommand command) {
        apply(new SpuRemovedEvent(
                command.getSpuId(), command.getUpdatedBy()
        ));
    }

    @EventSourcingHandler
    private void on(SpuRemovedEvent event) {
        if (YesNoEnum.N.equals(getRemoved())) {
            setRemoved(YesNoEnum.Y);
            setRemovedBy(event.getUpdatedBy());
            setRemovedAt(event.getUpdatedAt());
            setUpdatedBy(event.getUpdatedBy());
            setUpdatedAt(event.getUpdatedAt());
        }
    }

    @CommandHandler
    private void handle(ApproveSpuCommand command) {
        apply(new SpuApprovedEvent(
                command.getSpuId(), command.getUpdatedBy()
        ));
    }

    @EventSourcingHandler
    private void on(SpuApprovedEvent event) {
        if (ApprovalState.REVIEWING.equals(getApprovalState())) {
            setApprovalState(ApprovalState.APPROVED);
            setApprovedBy(event.getUpdatedBy());
            setApprovedAt(event.getUpdatedAt());
            setUpdatedBy(event.getUpdatedBy());
            setUpdatedAt(event.getUpdatedAt());
        }
    }

    @CommandHandler
    private void handle(RejectSpuCommand command) {
        apply(new SpuRejectedEvent(
                command.getSpuId(), command.getUpdatedBy()
        ));
    }

    @EventSourcingHandler
    private void on(SpuRejectedEvent event) {
        if (ApprovalState.REVIEWING.equals(getApprovalState())) {
            setApprovalState(ApprovalState.REJECTED);
            setRefusedBy(event.getUpdatedBy());
            setRefusedAt(event.getUpdatedAt());
            setUpdatedBy(event.getUpdatedBy());
            setUpdatedAt(event.getUpdatedAt());
        }
    }
}
